From 8a19ee7a2628f3881779928c20cdaf37c401b81a Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Tue, 24 Feb 2026 13:11:14 +0200 Subject: [PATCH] [PATCH 03/24] lib-mail: Limit the number of RFC2231 parameters that can be parsed This avoids excessive CPU usage especially in result_append(). Gbp-Pq: Name CVE-2026-27859.patch --- src/lib-mail/rfc2231-parser.c | 4 +++- src/lib-mail/rfc822-parser.h | 5 +++++ src/lib-mail/test-rfc2231-parser.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/lib-mail/rfc2231-parser.c b/src/lib-mail/rfc2231-parser.c index 2635a9a..80e0767 100644 --- a/src/lib-mail/rfc2231-parser.c +++ b/src/lib-mail/rfc2231-parser.c @@ -203,7 +203,7 @@ int rfc2231_parse(struct rfc822_parser_context *ctx, struct rfc2231_parameter rfc2231_param; const char *key, *p, *p2; string_t *str; - unsigned int i, j, count, next, next_idx; + unsigned int i, j, count, next, next_idx, params_count = 0; bool ok, broken = FALSE; const char *prev_replacement_str; int ret; @@ -221,6 +221,8 @@ int rfc2231_parse(struct rfc822_parser_context *ctx, t_array_init(&rfc2231_params_arr, 8); str = t_str_new(64); while ((ret = rfc822_parse_content_param(ctx, &key, str)) != 0) { + if (++params_count > RFC2231_MAX_PARAMS) + break; if (ret < 0) { /* try to continue anyway.. */ broken = TRUE; diff --git a/src/lib-mail/rfc822-parser.h b/src/lib-mail/rfc822-parser.h index 84e9bda..8d62387 100644 --- a/src/lib-mail/rfc822-parser.h +++ b/src/lib-mail/rfc822-parser.h @@ -3,6 +3,11 @@ #include "unichar.h" +/* Maximum number of parameters to parse. After this the rest of the parameters + are skipped. This is to avoid excessive CPU usage that can be caused by + merging of these parameters. */ +#define RFC2231_MAX_PARAMS 128 + /* This can be used as a common NUL replacement character */ #define RFC822_NUL_REPLACEMENT_STR UNICODE_REPLACEMENT_CHAR_UTF8 diff --git a/src/lib-mail/test-rfc2231-parser.c b/src/lib-mail/test-rfc2231-parser.c index 76b62a0..3536bb1 100644 --- a/src/lib-mail/test-rfc2231-parser.c +++ b/src/lib-mail/test-rfc2231-parser.c @@ -1,6 +1,7 @@ /* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ #include "lib.h" +#include "str.h" #include "rfc822-parser.h" #include "rfc2231-parser.h" #include "test-common.h" @@ -249,6 +250,34 @@ static void test_rfc2231_parser_multi_encodings(void) run_test("rfc2231 parser invalid encoding", input, sizeof(input)-1, output); } +static void test_rfc2231_parser_limits(void) +{ + string_t *input = t_str_new(1024); + + test_begin("rfc2231 parser limits"); + str_append(input, "; "); + for (unsigned int i = 0; i < 1100; i++) + str_printfa(input, "a%u=b%u; ", i, i); + struct rfc822_parser_context parser; + const char *const *result; + rfc822_parser_init(&parser, str_data(input), str_len(input), NULL); + test_assert(rfc2231_parse(&parser, &result) == 0); + + unsigned int count = str_array_length(result); + test_assert(count == RFC2231_MAX_PARAMS * 2); + for (unsigned int i = 0; i < count; i += 2) { + str_truncate(input, 0); + str_printfa(input, "a%u", i / 2); + test_assert_strcmp_idx(result[i], str_c(input), i); + + str_truncate(input, 0); + str_printfa(input, "b%u", i / 2); + test_assert_strcmp_idx(result[i + 1], str_c(input), i); + } + rfc822_parser_deinit(&parser); + test_end(); +} + int main(void) { static void (*const test_functions[])(void) = { @@ -264,6 +293,7 @@ int main(void) test_rfc2231_parser_encodings, test_rfc2231_parser_utf8_encoding, test_rfc2231_parser_multi_encodings, + test_rfc2231_parser_limits, NULL }; return test_run(test_functions); -- 2.30.2